package agent.strategy;

import oracle.MoveTypes;

public class exploreStrategy extends Strategy{

	/** This is how long we are going to be committed for */
	public int commitTime = 10;
	/** This is our decay factor */
	public double decayFactor = .5;
	/** calculates the weighted values of each tile */
	double[][] weights = new double[maxRow][maxCol];
	
	double threshholdExplore = .1;
	
	/** 
	 * override the default constructor
	 * 
	 * @param myBoard
	 */
	public exploreStrategy(BoardMemory myBoard) {
		super(myBoard);
	}
	
	/**
	 * This returns the next move which should be related to explored
	 * @param param1
	 * @param param2
	 * @param param3
	 */
	public nextDirObject nextMove(double param1, double param2, double param3){
		this.setUpFields();
		weights = new double[maxRow][maxCol];
		/* this says how many we can expose at each place */
		int numExposeAt[][] = new int[maxRow][maxCol];
		/* this says how how many spots it can be made visible from */
		int numVisibleFrom[][] = this.getNumPlacesExloredFrom(maxRow, maxCol);
		/* the probability of reaching the tile */
		double[][] probReaching = new double[maxRow][maxCol];
		/* our estimate on the utility of the square */
		double[][] utilToExploreAt = new double[maxRow][maxCol];
		
		nextDirObject myMove = new nextDirObject();
		myMove.adviseMove = false;
		
		/* fill in the weights for this board */
		for(int r=0; r<maxRow; r++){
			for(int c=0; c<maxCol; c++){
				weights[r][c] = this.decayedUtility(r, c); 
			}
		}
		double percentExplored = 0.9;
		
		if(this.shouldExploreFrom(this.currRow, this.currColumn, 1, numExposeAt[this.currRow][this.currColumn],
				commitTime,	20, numVisibleFrom[this.currRow][this.currColumn], this.numRunsRemaining, 
				percentExplored, true)){
			// we should explore from here
			myMove.adviseMove  = true;
			myMove.moveDirection = MoveTypes.LOOK_AROUND;
			return myMove;
		}
		
		/* calculate a value to send into our function to maximize our utility */
		for(int r=0; r<maxRow; r++){
			for(int c=0; c<maxCol; c++){
				probReaching[r][c]= probReachSquareFromTo(this.currRow,this.currColumn, r, c, commitTime).probSuccess;
				numExposeAt[r][c] = numWillExpose(r, c);
				double totalValExposed = sumValuesMadeVisibleAt(r,c);
				utilToExploreAt[r][c] = this.getEstimateOnUtility(r, c, probReaching[r][c], numExposeAt[r][c], 
						commitTime, Math.max(this.numMovesRemaining-commitTime, 0), commitTime, 
						this.numRunsRemaining, this.totalNumExposed()/((double)(maxRow*maxCol)), false);
			}
		}
		
		
		/* now maximize our utilty */
		myMove = this.getNextDirection(utilToExploreAt, new boolean[maxRow][maxCol], walls, 
				null, null, this.currRow, this.currColumn,
				commitTime, 0, this.maxRow, this.maxCol,
				10, 0);
		myMove.adviseMove = true;
		return myMove;
	}
	
	/**
	 *  THis loops through weights and sums up the values that an explore at r,c will do
	 */
	private double sumValuesMadeVisibleAt(int row, int col) {
		double sum = 0;
		for(int r=0; r<maxRow; r++){
			for(int c=0; c<maxCol; c++){
				if(this.isReachable(r, c, row, col))
					sum+= weights[r][c];
			}
		}
		return sum;
	}

	/**
	 * This returns a matrix that says from how many tiles the [r,c] can be made visible
	 * @param maxRow The max number of rows
	 * @param maxCol The max number of columns
	 * @return numMadeVisibleBy[][]
	 */
	public int[][] getNumPlacesExloredFrom(int maxRow, int maxCol){
		/* numFrom[r][c] holds how many spots (r,c) can be made visible by a look_around move */
		int[][] numFrom = new int[maxRow][maxCol];
		for(int r=0; r<maxRow; r++){
			for(int c=0; c<maxCol; c++){
				for(int row=0; row<maxRow; row++){
					for(int col=0; col<maxCol; col++){
						if(this.isReachable(r, c, row, col)){
							numFrom[r][c]++;
						}
					}
				}
			}
		}
		
		return numFrom;
	}
	
	

	/**
	 * This is a function that we get to optimize, crazy function, it might work
	 * This tells us the value of exploring from this location
	 * @param row	The row we are at
	 * @param col	The column we are at
	 * @param probReaching	The probability of reaching this spot in the allotted amount of moves
	 * @param numExposeAt How many tiles we will reveal at that position
	 * @param movesAllowed	Moves allowed to reach this tile
	 * @param movesRemainingAfter	How many moves in this same run we will have left
	 * @param maxPossibleToExplore	The most ever we could ever expose, optimal placement on board
	 * @param RunsRemaining	How many runs we have left through this same map
	 * @param percentExplored Percent of the total map that we have explored
	 * @param isVisible true if this 
	 * @return
	 */
	public double getEstimateOnUtility(int row, int col, double probReaching, double numExposeAt, int movesAllowed, 
			double movesRemainingAfter, int maxPossibleToExplore, int RunsRemaining, double percentExplored,
			boolean isVisible){
		
		int numberTimesSawTile = getNumTimesSawTile(row, col);
		int howLongSinceLastSeen = getNumTimesSinceSeen(row,col);
		
		if(isVisible)	// if we know what is there
			return 0;
		
		return Math.pow(decayFactor, howLongSinceLastSeen);
	}

	
	/**
	 * This is a simple utility function which looks at if its visible
	 * and how long since it was last visible
	 * @param row
	 * @param col
	 * @return
	 */
	public double decayedUtility(int row, int col){
		boolean isVisible = this.isVisible(row,col);
		int numberTimesSawTile = getNumTimesSawTile(row, col);
		int howLongSinceLastSeen = getNumTimesSinceSeen(row,col);
		if(isVisible)	// if we know what is there
			return 0;	
		return Math.pow(decayFactor, howLongSinceLastSeen);
	}
	
	/**
	 * Returns true if this tile is visible
	 * @param row
	 * @param col
	 * @return
	 */
	private boolean isVisible(int row, int col) {
		if( this.myAgent.oracle.getTileByPosition(row, col) == null)
			return true;
		return false;
	}

	/**
	 * Returns true if we should explore from this location
	 * @param row	The row we are at
	 * @param col	The column we are at
	 * @param probReaching	The probability of reaching this spot in the allotted amount of moves
	 * @param numExposeAt How many tiles we will reveal at that position
	 * @param movesAllowed	Moves allowed to reach this tile
	 * @param movesRemainingAfter	How many moves in this same run we will have left
	 * @param maxPossibleToExplore	The most ever we could ever expose, optimal placement on board
	 * @param RunsRemaining	How many runs we have left through this same map
	 * @param percentExplored Percent of the total map that we have explored
	 * @param isVisible true if this 
	 * @return
	 */
	public boolean shouldExploreFrom(int row, int col, double probReaching, double numExposeAt, int movesAllowed, 
			double movesRemainingAfter, int maxPossibleToExplore, int RunsRemaining, double percentExplored,
			boolean isVisible){
		
		double maxPoss = (double) this.getMaxPossTilesEver();
		double sumExpose = this.sumValuesMadeVisibleAt(row, col);
		System.out.println(" value of exp " + probReaching + "  " + sumExpose + "    " +  maxPoss);
		if(probReaching*sumExpose/maxPoss > threshholdExplore)
			return true;
		
		return false;
	}

	/**
	 * returns how many rounds since we last saw it
	 * @param row
	 * @param col
	 * @return
	 */
	private int getNumTimesSinceSeen(int row, int col) {
		int time = 10;
		return 1;
	}

	/**
	 * Returns the number of times that we saw this tile
	 * @param row
	 * @param col
	 * @return
	 */
	private int getNumTimesSawTile(int row, int col) {
		return 0;
	}
	
}
